package org.yzhzc.job.handler.exam;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.yzhzc.common.core.constants.CacheConstants;
import org.yzhzc.common.core.constants.Constants;
import org.yzhzc.common.redis.service.RedisService;
import org.yzhzc.job.domain.exam.entity.Exam;
import org.yzhzc.job.domain.message.entity.Message;
import org.yzhzc.job.domain.message.entity.MessageText;
import org.yzhzc.job.domain.message.vo.MessageTextVO;
import org.yzhzc.job.domain.user.dto.UserScoreDTO;
import org.yzhzc.job.mapper.exam.ExamMapper;
import org.yzhzc.job.mapper.message.MessageTextMapper;
import org.yzhzc.job.mapper.user.UserExamMapper;
import org.yzhzc.job.mapper.user.UserSubmitMapper;
import org.yzhzc.job.service.message.IMessageService;
import org.yzhzc.job.service.message.IMessageTextService;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class ExamXxlJob {

    // 定义注入的 Mapper 和 Service，用于数据库操作和缓存操作
    @Autowired
    private ExamMapper examMapper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private IMessageTextService messageTextService;
    @Autowired
    private IMessageService messageService;
    @Autowired
    private UserSubmitMapper userSubmitMapper;
    @Autowired
    private MessageTextMapper messageTextMapper;
    @Autowired
    private UserExamMapper userExamMapper;

    /**
     * XXL-JOB任务：竞赛列表重置
     * 统计哪些竞赛属于未完赛列表，哪些属于历史竞赛列表，并刷新缓存
     */
    @XxlJob("examListOrganizeHandler")
    public void examListOrganizeHandler() {
        log.info("*** examListOrganizeHandler ***");

        // 查询未完赛的竞赛
        List<Exam> unFinishList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                .gt(Exam::getEndTime, LocalDateTime.now()) // 结束时间大于当前时间
                .eq(Exam::getStatus, Constants.TRUE) // 状态为有效
                .orderByDesc(Exam::getCreateTime));

        // 刷新未完赛竞赛缓存
        refreshCache(unFinishList, CacheConstants.EXAM_UNFINISHED_LIST);

        // 查询历史竞赛
        List<Exam> historyList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                .le(Exam::getEndTime, LocalDateTime.now()) // 结束时间小于或等于当前时间
                .eq(Exam::getStatus, Constants.TRUE)
                .orderByDesc(Exam::getCreateTime));

        // 刷新历史竞赛缓存
        refreshCache(historyList, CacheConstants.EXAM_HISTORY_LIST);

        log.info("*** examListOrganizeHandler 统计结束 ***");
    }

    /**
     * XXL-JOB任务：处理竞赛结果
     * 计算每场竞赛的排名并生成用户消息，保存到数据库和缓存
     */
    @XxlJob("examResultHandler")
    public void examResultHandler() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime minusDateTime = now.minusDays(1);

        // 查询在过去一天内刚结束的竞赛
        List<Exam> examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId, Exam::getTitle)
                .eq(Exam::getStatus, Constants.TRUE)
                .ge(Exam::getEndTime, minusDateTime) // 结束时间大于等于一天前
                .le(Exam::getEndTime, now)); // 结束时间小于等于当前时间

        if (CollectionUtil.isEmpty(examList)) {
            return; // 如果没有符合条件的竞赛，直接返回
        }

        // 提取竞赛ID集合
        Set<Long> examIdSet = examList.stream().map(Exam::getExamId).collect(Collectors.toSet());

        // 查询用户分数
        List<UserScoreDTO> userScoreList = userSubmitMapper.selectUserScoreList(examIdSet);

        // 将用户分数按竞赛ID分组
        Map<Long, List<UserScoreDTO>> userScoreMap = userScoreList.stream()
                .collect(Collectors.groupingBy(UserScoreDTO::getExamId));

        // 根据分数生成消息
        createMessage(examList, userScoreMap);
    }

    /**
     * 创建用户消息
     * @param examList 竞赛列表
     * @param userScoreMap 用户分数按竞赛分组的Map
     */
    private void createMessage(List<Exam> examList, Map<Long, List<UserScoreDTO>> userScoreMap) {
        List<MessageText> messageTextList = new ArrayList<>();
        List<Message> messageList = new ArrayList<>();

        for (Exam exam : examList) {
            Long examId = exam.getExamId();
            List<UserScoreDTO> userScoreList = userScoreMap.get(examId);
            int totalUser = userScoreList.size();
            int examRank = 1;

            // 遍历用户分数，生成排名和消息
            for (UserScoreDTO userScore : userScoreList) {
                String msgTitle = exam.getTitle() + "——排名情况";
                String msgContent = "您所参与的竞赛：" + exam.getTitle()
                        + "，本次参与竞赛一共" + totalUser + "人，您排名第" + examRank + "名！";

                userScore.setExamRank(examRank); // 设置用户排名

                // 构造消息内容
                MessageText messageText = new MessageText();
                messageText.setMessageTitle(msgTitle);
                messageText.setMessageContent(msgContent);
                messageText.setCreateBy(Constants.SYSTEM_USER_ID);
                messageTextList.add(messageText);

                // 构造用户消息
                Message message = new Message();
                message.setSendId(Constants.SYSTEM_USER_ID);
                message.setCreateBy(Constants.SYSTEM_USER_ID);
                message.setRecId(userScore.getUserId());
                messageList.add(message);

                examRank++;
            }

            // 更新用户的分数和排名
            userExamMapper.updateUserScoreAndRank(userScoreList);

            // 将排名数据存入Redis缓存
            redisService.rightPushAll(getExamRankListKey(examId), userScoreList);
        }

        // 批量插入消息内容
        messageTextService.batchInsert(messageTextList);

        // 将消息内容与用户消息关联
        Map<String, MessageTextVO> messageTextVOMap = new HashMap<>();
        for (int i = 0; i < messageTextList.size(); i++) {
            MessageText messageText = messageTextList.get(i);
            MessageTextVO messageTextVO = new MessageTextVO();
            BeanUtil.copyProperties(messageText, messageTextVO);

            String msgDetailKey = getMsgDetailKey(messageText.getTextId());
            messageTextVOMap.put(msgDetailKey, messageTextVO);

            Message message = messageList.get(i);
            message.setTextId(messageText.getTextId());
        }

        // 批量插入用户消息
        messageService.batchInsert(messageList);

        // 将用户消息数据存入Redis缓存
        Map<Long, List<Message>> userMsgMap = messageList.stream()
                .collect(Collectors.groupingBy(Message::getRecId));

        for (Map.Entry<Long, List<Message>> entry : userMsgMap.entrySet()) {
            Long recId = entry.getKey();
            String userMsgListKey = getUserMsgListKey(recId);
            List<Long> userMsgTextIdList = entry.getValue().stream()
                    .map(Message::getTextId).toList();

            redisService.rightPushAll(userMsgListKey, userMsgTextIdList);
        }

        redisService.multiSet(messageTextVOMap);
    }

    /**
     * 刷新缓存
     * @param examList 竞赛列表
     * @param examListKey 缓存Key
     */
    public void refreshCache(List<Exam> examList, String examListKey) {
        if (CollectionUtil.isEmpty(examList)) {
            return;
        }

        Map<String, Exam> examMap = new HashMap<>();
        List<Long> examIdList = new ArrayList<>();

        // 构建详情缓存数据和ID列表
        for (Exam exam : examList) {
            examMap.put(getDetailKey(exam.getExamId()), exam);
            examIdList.add(exam.getExamId());
        }

        // 更新详情缓存和列表缓存
        redisService.multiSet(examMap);
        redisService.deleteObject(examListKey);
        redisService.rightPushAll(examListKey, examIdList);
    }

    // 构造各类缓存Key的私有方法
    private String getDetailKey(Long examId) {
        return CacheConstants.EXAM_DETAIL + examId;
    }

    private String getUserMsgListKey(Long userId) {
        return CacheConstants.USER_MESSAGE_LIST + userId;
    }

    private String getMsgDetailKey(Long textId) {
        return CacheConstants.MESSAGE_DETAIL + textId;
    }

    private String getExamRankListKey(Long examId) {
        return CacheConstants.EXAM_RANK_LIST + examId;
    }
}
